﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using RecruitmentAgency.Models.DataTypes;
using RecruitmentAgency.Models.Utilities;
using RecruitmentAgency.Models.Validation;

namespace RecruitmentAgency.Controllers
{
    ///<summary>
    /// The RecruitmentAgency.Controllers.AccountController provides interaction for the Logon pages on the RecruitmentAgency web site
    ///</summary>
    [HandleError]
    public class AccountController : BaseController
    {
        private readonly AccountValidator validator;
        private const string SelectMessage = "Please select an option from the list";
                
        ///<summary>
        /// Initialises a new RecruitmentAgency.Controllers.AccountController 
        /// This constructor is used by the MVC framework to instantiate the controller using
        /// the default forms authentication and membership providers.
        ///</summary>
        public AccountController() 
        {
            ViewData["titles"] = new SelectList(CommonMethods.AddSelectMessageToList(DataProvider.GetTitles()));
            ViewData["authorityToWork"] = new SelectList(CommonMethods.AddSelectMessageToList(DataProvider.GetAuthorityToWork()));
            ViewData["contactPreference"] = new SelectList(CommonMethods.AddSelectMessageToList(DataProvider.GetContactPreferences()));
            validator = new AccountValidator(ModelState, SelectMessage, MinimumPasswordLength);
        }
                       
        ///<summary>
        /// This GET LogOn method sets the ViewData and returns a LoginData 
        ///</summary>
        ///<returns>Returns a LoginData </returns>
        [AcceptVerbs(HttpVerbs.Get)]
        public ActionResult LogOn()
        {
            SetViewData();
            LoginData loginData = new LoginData
                                      {
                                          Login = new Login(),
                                          Register = new Register(),
                                          RegisterJobProvider = new RegisterJobProvider()
                                      };
            return View(loginData);
        }

        /// <summary>
        /// Set the ViewData according to the query string
        /// </summary>
        /// <returns>Returns the ViewData["panel"] value as string </returns>
        public string SetViewData()
        {            
            if (Request.QueryString["Type"] == "Register") ViewData["panel"] = "Register";
            else if (Request.QueryString["Type"] == "JobProvider") ViewData["panel"] = "JobProvider";
            else ViewData["panel"] = "LogIn";
            return ViewData["panel"].ToString();
        }

        ///<summary>
        /// This POST LogOn manage the login for the various website user types
        ///</summary>
        ///<param name="login">A Login instance containing the login details</param>
        ///<param name="register">A Register instance containing the registration details for a Job Seeker</param>
        ///<param name="jobProvider">A JobProvider instance containing the registration details for a Job Provider</param>
        ///<returns></returns>
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult LogOn(Login login, Register register, RegisterJobProvider jobProvider)
        {
            LoginData loginData = new LoginData();
            loginData.Login = login;
            loginData.Register = register;
            loginData.RegisterJobProvider = jobProvider;
            ViewData["PasswordLength"] = MinimumPasswordLength;                     
            MembershipCreateStatus createStatus;
            SetViewData();
            if (validator.ValidateLogOn(login))
            {
                if (AccountDataProvider.ValidateUser(login.UserName, login.Password))
                {
                    SetAuthentication(login.UserName, login.RememberMe);
                    return !string.IsNullOrEmpty(login.ReturnUrl) ? (ActionResult)Redirect(login.ReturnUrl) : RedirectToAction("Index", "Home");
                }
                else ModelState.AddModelError("Login", "Either the username or the password provided is incorrect.");
            }
            if (validator.ValidateRegistration(register))
            {
                if (AccountDataProvider.UserNameIsUnique(register.RegisterUserName))
                {
                    Dictionary<byte[], byte[]> encryptedData = Encryption.Encrypt(register.RegisterUserName, register.RegisterPassword);
                    register.RegisterSalt = encryptedData.ElementAt(0).Key;
                    register.RegisterEncryptedPassword = encryptedData.ElementAt(0).Value;
                    // Attempt to register the user as a job seeker
                    createStatus = AccountDataProvider.CreateUser(register);
                    if (createStatus == MembershipCreateStatus.Success)
                    {
                        SetAuthentication(register.RegisterUserName, true);
                        return RedirectToAction("Index", "Home");
                    }
                    else ModelState.AddModelError("RegisterJobSeeker", AccountDataProvider.ErrorCodeToString(createStatus));
                }
                else ModelState.AddModelError("RegisterUserName", "That username is already in use.");
            }
            if (validator.ValidateRegistrationJobProvider(jobProvider))
            {
                if (AccountDataProvider.UserNameIsUnique(register.RegisterUserName))
                {
                    Dictionary<byte[], byte[]> encryptedData = Encryption.Encrypt(jobProvider.JobProviderUserName, jobProvider.JobProviderPassword);
                    jobProvider.Salt = encryptedData.ElementAt(0).Key;
                    jobProvider.EncryptedPassword = encryptedData.ElementAt(0).Value;
                    // Attempt to register the user as a job provider
                    createStatus = AccountDataProvider.CreateJobProvider(jobProvider);
                    if (createStatus == MembershipCreateStatus.Success)
                    {
                        SetAuthentication(jobProvider.JobProviderUserName, true);
                        return RedirectToAction("Index", "Home");
                    }
                    else ModelState.AddModelError("registerJobProvider", AccountDataProvider.ErrorCodeToString(createStatus));
                }
                else ModelState.AddModelError("RegisterUserName", "That username is already in use.");
            }
            return View(loginData);
        }

        /// <summary>
        /// Create a FormsAuthentication nstance that stores the user details for the session
        /// </summary>
        /// <param name="username">The user username</param>
        /// <param name="rememberMe">If true set the cookie to remeber the user details</param>
        private void SetAuthentication(string username, bool rememberMe)
        {
            FormsAuthentication.Initialize();
            PersonalDetail personalDetail = DataProvider.GetPersonByUsername(username);
            string userData = personalDetail.PersonId + "," + personalDetail.RoleId;           
            //The AddMinutes determines how long the user will be logged in after leaving
            //the site if he doesn't log off.
            //userData personId, roleId            
            FormsAuthentication.SetAuthCookie(username, rememberMe);
            FormsAuthenticationTicket fat = new FormsAuthenticationTicket(1, username, DateTime.Now, DateTime.Now.AddMinutes(30), rememberMe, userData, FormsAuthentication.FormsCookiePath);            
            Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(fat)));
            // Create an Identity object
            FormsIdentity id = new FormsIdentity(fat);
            // This principal will flow throughout the request.            
            GenericPrincipal principal = new GenericPrincipal(id, personalDetail.RoleId.ToString().Split(new[] { '|' }));
            // Attach the new principal object to the current HttpContext object
            HttpContext.User = principal;    
        }

        ///<summary>
        /// Sign out the user removing the FormsAuthentication instance containing the user details from the session and redirect to the Home page
        ///</summary>
        ///<returns>Redirect the user to the website Home page</returns>
        public ActionResult LogOff()
        {
            FormsAuthentication.SignOut();
            ResetPageSize();
            return RedirectToAction("Index", "Home");
        }
    }
}